﻿#ifndef MYSERIAL_H
#define MYSERIAL_H

#include <QComboBox>
#include <QWidget>
#include <QSerialPort>
#include <QSerialPortInfo>
#include <QTimer>
#include <QSet>

#define FUNC_VERSION_CODE 0x81
#define FUNC_FIRMWAREINFO_CODE 0x82
#define FUNC_ERASE_CODE 0x83
#define FUNC_FIRMWAREDATA_CODE 0x84

#define ACK_FUNC_VERSION_CODE 0xF1
#define ACK_FUNC_FIRMWAREINFO_CODE 0xF2
#define ACK_FUNC_ERASE_CODE 0xF3
#define ACK_FUNC_ERASEDONE_CODE 0xF4
#define ACK_FUNC_FIRMWAREDATA_CODE 0xF5
#define ACK_FUNC_FIRMWARECRCOK_CODE 0xF6

class mySerial : public QObject
{
    Q_OBJECT
public:
    /* 串口类的构造函数 传入下拉框 */
    explicit mySerial(QComboBox *comboBox, QObject *parent = nullptr);

    /* 打开串口 传入串口名称 波特率 数据位 停止位 校验位 （默认115200 8位 1位停止位 无校验） */
    bool openPort(const QString &portName,
                      qint32 baudRate = QSerialPort::Baud115200,
                      QSerialPort::DataBits dataBits = QSerialPort::Data8,
                      QSerialPort::StopBits stopBits = QSerialPort::OneStop,
                      QSerialPort::Parity parity = QSerialPort::NoParity);

    /* 关闭当前串口 */
    void closeSerialPort();

    /* 获取当前打开的串口 */
    QSerialPort *currentSerialPort();

    /* 组合数据包 */
    QByteArray makeDataPacket(quint8 funcCode, const QByteArray &dataPack);
    /* 发送数据包 */
    bool sendDataPacket(quint8 funcCode, const QByteArray &dataPack);
    /* 自动重发函数 3次 间隔500ms */
    void sendDataPacketRetry(quint8 sendFuncCode, quint8 ackFuncCode, const QByteArray &dataPack, const QString crc);
    void sendDataPacketRetryFrimwareData(quint8 sendFuncCode, quint8 ackFuncCode, const QByteArray &dataPack, const QString crc);
    /* 设置需要等待的应答码 */
    void setWaitFuncCode(quint8 funcCode);

signals:
    /* 检测到新增串口 */
    void portAdded(const QString &portName);
    /* 检测到串口移除 */
    void portRemoved(const QString  &portName);
    /* 已打开的串口被移除 */
    void portDisconnectde();
    /* 串口打开成功 */
    void portOpend(const QString &portName);
    /* 串口关闭 */
    void portClosed(const QString &portName);

    /* 接收到应答 */
    void ackReceived(quint8 funcCode, const QByteArray &data);
    /* 未收到应答 */
    void noAckReceived(quint8 funcCode);
    /* 未收到应答 */
    void checksumError(quint8 funcCode);

private slots:
    /* 跟新串口变化 */
    void updateSerialPort();
    /* 串口读取信号槽 */
    void onReadRead();
    /* 发送一次 */
    void sendFirst();

private:
    /* 更新显示 */
    void refreshComboBox();

    /* 解析数据包 */
    void parseDataPacket();

    /* 上层接收数据包处理 */
    void handleReceivedPacket(quint8 funcCode, const QByteArray &data);

private:
    /* 串口定时扫描定时器 */
    QTimer *timer;
    /* 显示串口的下拉框 */
    QComboBox *combox;
    /* 已打开的串口 */
    QSerialPort *activeSerialPort;
    /* 当前可用的串口列表 */
    QStringList serialPortList;

    /* 串口重发定时器 */
    QTimer *retryTimer = nullptr;
    /* 重发次数 */
    quint8 retryCount = 0;
    /* 等待发送的功能码 */
    quint8 waitSendFuncCode = 0;
    /* 等待应答的功能码 */
    quint8 waitAckFuncCode = 0;
    /* 需要发送的数据 */
    QByteArray waitSendData;
    /* 接收数据缓冲区 */
    QByteArray rxBuffer;
    /* 是否收到应答 */
    bool ackReceivedState = false;
    /* crc算法 */
    QString crcSelect = "CRC-8";
    /* 标记是哪个重发函数触发的 以便于重新启动定时器的正确性 */
    quint8 whichTimeStrike;
};

#endif // MYSERIAL_H
